import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { WsException } from '@nestjs/websockets';
import { Socket } from 'socket.io';

@Catch()
export class CasinoExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(CasinoExceptionFilter.name);

  catch(exception: unknown, host: ArgumentsHost) {
    const contextType = host.getType();

    if (contextType === 'ws') {
      return this.handleWebSocketException(exception, host);
    } else {
      return this.handleHttpException(exception, host);
    }
  }

  /**
   * 处理HTTP异常
   */
  private handleHttpException(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    let status = HttpStatus.INTERNAL_SERVER_ERROR;
    let message = '内部服务器错误';
    let errorCode = -1;

    if (exception instanceof HttpException) {
      status = exception.getStatus();
      const responseBody = exception.getResponse();

      if (typeof responseBody === 'object' && responseBody !== null) {
        message = (responseBody as any).message || exception.message;
        errorCode = (responseBody as any).errorCode || -1;
      } else {
        message = exception.message;
      }
    } else if (exception instanceof Error) {
      message = exception.message;

      // 根据错误类型设置状态码
      if (exception.name === 'ValidationError') {
        status = HttpStatus.BAD_REQUEST;
        errorCode = 9; // 参数错误
      } else if (exception.name === 'UnauthorizedError') {
        status = HttpStatus.UNAUTHORIZED;
        errorCode = 401;
      } else if (exception.name === 'ForbiddenError') {
        status = HttpStatus.FORBIDDEN;
        errorCode = 403;
      }
    }

    // 记录错误日志
    this.logger.error(
      `HTTP异常: ${request.method} ${request.url} - ${status} - ${message}`,
      exception instanceof Error ? exception.stack : undefined,
    );

    // 构建错误响应
    const errorResponse = {
      act_id: this.extractActId(request),
      code: errorCode,
      msg: message,
      timestamp: new Date().toISOString(),
      path: request.url,
    };

    response.status(status).json(errorResponse);
  }

  /**
   * 处理WebSocket异常
   */
  private handleWebSocketException(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToWs();
    const client = ctx.getClient<Socket>();

    let message = 'WebSocket错误';
    let errorCode = -1;

    if (exception instanceof WsException) {
      message = exception.message;
      errorCode = 8; // WebSocket错误
    } else if (exception instanceof Error) {
      message = exception.message;

      // 根据错误类型设置错误码
      if (exception.name === 'ValidationError') {
        errorCode = 9; // 参数错误
      } else if (exception.name === 'UnauthorizedError') {
        errorCode = 401;
      } else if (exception.name === 'ForbiddenError') {
        errorCode = 403;
      }
    }

    // 记录错误日志
    this.logger.error(
      `WebSocket异常: 客户端 ${client.id} - ${errorCode} - ${message}`,
      exception instanceof Error ? exception.stack : undefined,
    );

    // 构建错误响应
    const errorResponse = {
      act_id: 0, // 通用错误响应
      code: errorCode,
      msg: message,
      timestamp: new Date().toISOString(),
    };

    // 发送错误响应给客户端
    client.emit('error', errorResponse);
  }

  /**
   * 从请求中提取act_id
   */
  private extractActId(request: Request): number {
    try {
      // 尝试从URL参数获取
      const urlActId = request.params.act_id || request.query.act_id;
      if (urlActId) {
        return parseInt(urlActId.toString());
      }

      // 尝试从请求体获取
      if (request.body && request.body.act_id) {
        return parseInt(request.body.act_id);
      }

      // 尝试从查询参数获取
      const queryActId = request.query.c;
      if (queryActId) {
        return parseInt(queryActId.toString());
      }

      return 0; // 默认值
    } catch (error) {
      return 0;
    }
  }
}

/**
 * 娱乐城业务异常类
 */
export class CasinoBusinessException extends Error {
  constructor(
    public readonly message: string,
    public readonly errorCode: number = 8,
    public readonly actId?: number,
  ) {
    super(message);
    this.name = 'CasinoBusinessException';
  }
}

/**
 * 娱乐城验证异常类
 */
export class CasinoValidationException extends Error {
  constructor(
    public readonly message: string,
    public readonly field?: string,
    public readonly actId?: number,
  ) {
    super(message);
    this.name = 'CasinoValidationException';
  }
}

/**
 * 娱乐城权限异常类
 */
export class CasinoPermissionException extends Error {
  constructor(
    public readonly message: string = '权限不足',
    public readonly actId?: number,
  ) {
    super(message);
    this.name = 'CasinoPermissionException';
  }
}

/**
 * 娱乐城资源不存在异常类
 */
export class CasinoResourceNotFoundException extends Error {
  constructor(
    public readonly resource: string,
    public readonly resourceId: string | number,
    public readonly actId?: number,
  ) {
    super(`${resource} ${resourceId} 不存在`);
    this.name = 'CasinoResourceNotFoundException';
  }
}

/**
 * 娱乐城余额不足异常类
 */
export class CasinoInsufficientBalanceException extends Error {
  constructor(
    public readonly currency: string,
    public readonly required: number,
    public readonly available: number,
    public readonly actId?: number,
  ) {
    super(`${currency}余额不足，需要${required}，可用${available}`);
    this.name = 'CasinoInsufficientBalanceException';
  }
}

/**
 * 娱乐城游戏状态异常类
 */
export class CasinoGameStateException extends Error {
  constructor(
    public readonly gameType: string,
    public readonly expectedState: string,
    public readonly actualState: string,
    public readonly actId?: number,
  ) {
    super(`${gameType}游戏状态错误，期望${expectedState}，实际${actualState}`);
    this.name = 'CasinoGameStateException';
  }
} 